<?php
/**
 * Svick a Fast Simple Smart PHP FrameWork
 * Author: Tommy 863758705@qq.com
 * Link: http://svick.tomener.com/
 * Since: 2022
 */

namespace Svick\Core;


use Exception;
use stdClass;
use Svick\Config\Config;
use Svick\Exception\CoreException;
use Svick\Http\Response;
use Svick\Route\Route;

class App
{
    /**
     * 版本号
     */
    const VERSION = '2.0.2';

    /**
     * @var string 环境
     */
    public static $env = '';

    public static $envPath = '';

    /**
     * @var string 应用名
     */
    protected static $name = '';

    /**
     * @var string 控制器名
     */
    protected static $controller;

    /**
     * @var string 动作名
     */
    protected static $action;

    /**
     * @var string 参数
     */
    protected static $params;

    /**
     * @var Container 依赖注入容器
     */
    protected static $container;

    /**
     * 初始化
     */
    public static function init()
    {
        date_default_timezone_set('Asia/Chongqing');

        //检查环境版本
        version_compare(PHP_VERSION, '7.2.5', '>=') || die('requires PHP 7.2.5+ Please upgrade!');

        defined('DS') || define('DS', DIRECTORY_SEPARATOR);

        defined('ROOT_PATH') || die('[Svick] undefined ROOT_PATH constant.');

        define('FRAME_PATH', dirname(__DIR__, 2) . DS);

        //环境常量
        define('IS_CLI', PHP_SAPI == 'cli' ? 1 : 0);
        define('NOW_TIME', $_SERVER['REQUEST_TIME']);

        spl_autoload_register([__CLASS__, 'loadClass']);

        set_error_handler([__CLASS__, 'errorHandler']);
        set_exception_handler([CoreException::class, 'handle']);
    }

    /**
     * 启动应用
     *
     * @param string $name
     * @return void
     * @throws CoreException
     * @throws Exception
     */
    public static function run(string $name = '')
    {
        App::$name = $name;
        App::init();
        App::loadConfig();

        //路由
        Route::router()->dispatch();
        $route = Route::router()->getRoute();
        if (empty($route)) {
            throw new CoreException('The current route does not exist', 40001);
        }

        App::$controller = $route['controller'];
        App::$action = $route['action'];
        App::$params = $route['params'];

        $container = App::container();
        App::loadProvider($container);

        //请求对象
        $request = new \Svick\Http\Request();
        $request->app = App::$name;
        $request->controller = $route['controller'];
        $request->action = $route['action'];
        $request->setParams($route['params']);
        $container->instance(['Svick\Http\Request' => 'request'], $request);

        $data = $container->invokeMethod([$route['handler'], $route['action']], $route['args']);
        if ($data != null) {
            Response::default()->send($data);
        }
    }

    /**
     * 设置控制器、动作、参数
     *
     * @param string $controller
     * @param string $action
     * @param array $params
     */
    public static function iniSet(string $controller, string $action, array $params)
    {
        App::$name = APP_NAME;
        App::$controller = $controller;
        App::$action = $action;
        App::$params = $params;
    }

    /**
     * 设置应用名
     *
     * @param $name
     * @return void
     */
    public static function setName($name)
    {
        if (empty(static::$name)) {
            static::$name = $name;
        }
    }

    /**
     * 获取应用名
     *
     * @return string
     */
    public static function name(): string
    {
        return static::$name;
    }

    /**
     * 获取控制器名
     *
     * @return string
     */
    public static function controller(): string
    {
        return static::$controller;
    }

    /**
     * 获取动作名
     *
     * @return string
     */
    public static function action(): string
    {
        return static::$action;
    }

    /**
     * 获取IOC/DI容器
     *
     * @return Container
     */
    public static function container(): Container
    {
        if (is_null(static::$container)) {
            static::$container = new Container();
            static::$container->instance(['Svick\Core\Container' => 'container'], static::$container);
        }
        return static::$container;
    }

    /**
     * 加载配置文件
     * @throws Exception
     */
    public static function loadConfig()
    {
        $env_config = ROOT_PATH . 'config' . DS . 'env.config.php';
        if (!file_exists($env_config)) {
            die(sprintf("env file not exist: %s", $env_config));
        }

        $env = include $env_config;
        App::$env = $env['env'] ?? '';
        App::$envPath = !empty(App::$env) ? App::$env . DS : '';

        $config_files = [
            FRAME_PATH . 'config' . DS . 'config.php',
            'db',
            'common'
        ];
        $public_config_file = dirname(ROOT_PATH) . DS . 'conf' . DS . App::$envPath . 'db.config.php';
        if (file_exists($public_config_file)) {
            $config_files[] = $public_config_file;
        }
        foreach ($config_files as $config_file) {
            Config::load($config_file, 'runtime');
        }

        Config::load($env, 'runtime');

        defined('CACHE_PATH') || define('CACHE_PATH', Config::runtime('cache.path'));
    }

    /**
     * 应用路径
     *
     * @param string $path
     * @return string
     */
    public static function appPath(string $path = '')
    {
        return ROOT_PATH . 'app' . DS . $path;
    }

    /**
     * 配置文件路径
     *
     * @param string $path
     * @return string
     */
    public static function configPath(string $path = ''): string
    {
        return ROOT_PATH . 'config' . DS . static::$envPath . $path;
    }

    /**
     * 加载服务提供者
     *
     * @param Container $container
     */
    protected static function loadProvider(Container $container)
    {
        if ($providers = Config::runtime('providers')) {
            foreach ($providers as $provider) {
                $provider = $container->get($provider);
                $provider->register();
            }
        }
    }

    /**
     * 自动加载
     *
     * @param $class_name
     * @return bool
     */
    public static function loadClass($class_name): bool
    {
        $class_name = str_replace('\\', DIRECTORY_SEPARATOR, $class_name);
        $pos = strpos($class_name, DIRECTORY_SEPARATOR);
        $space = substr($class_name, 0, $pos);

        if ($space == 'Svick') {
            $class_name = substr($class_name, $pos + 1);
            $class_file = FRAME_PATH . 'src' . DS . $class_name . '.php';
        } else {
            $class_file = ROOT_PATH . $class_name . '.php';
        }

        if (file_exists($class_file)) {
            require $class_file;
            return true;
        }

        return true;
    }

    /**
     * 错误处理函数
     *
     * @param $code
     * @param $message
     * @param $file
     * @param $line
     */
    public static function errorHandler($code, $message, $file, $line)
    {
        $e = new CoreException($message, $code, $file, $line);
        CoreException::handle($e);
    }

    /**
     * 返回一个对象或JSON字符串
     *
     * @param int $code
     * @param string $msg
     * @param null $data
     * @return stdClass
     */
    public static function result(int $code = 1, string $msg = '', $data = null): stdClass
    {
        $r = new stdClass();
        $r->code = $code;
        $r->msg = $msg;
        if (!is_null($data)) {
            $r->data = $data;
        }

        return $r;
    }
}
